package com.example.javapoet.demo.advanced;

import com.squareup.javapoet.*;
import javax.lang.model.element.Modifier;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

/**
 * JavaPoet高级示例 - 生成反射相关代码
 */
public class CodeGenerationWithReflection {

    public static void main(String[] args) {
        // 生成反射工具类
        generateReflectionUtils();
        
        // 生成动态代理类
        generateDynamicProxyClass();
    }

    /**
     * 生成反射工具类
     */
    public static void generateReflectionUtils() {
        // 创建方法
        MethodSpec getFieldValueMethod = MethodSpec.methodBuilder("getFieldValue")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addTypeVariable(TypeVariableName.get("T"))
                .returns(TypeVariableName.get("T"))
                .addException(ReflectiveOperationException.class)
                .addParameter(Object.class, "obj")
                .addParameter(String.class, "fieldName")
                .addStatement("$T field = obj.getClass().getDeclaredField(fieldName)", Field.class)
                .addStatement("field.setAccessible(true)")
                .addStatement("return ($T) field.get(obj)", TypeVariableName.get("T"))
                .build();
        
        MethodSpec setFieldValueMethod = MethodSpec.methodBuilder("setFieldValue")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addException(ReflectiveOperationException.class)
                .addParameter(Object.class, "obj")
                .addParameter(String.class, "fieldName")
                .addParameter(Object.class, "value")
                .addStatement("$T field = obj.getClass().getDeclaredField(fieldName)", Field.class)
                .addStatement("field.setAccessible(true)")
                .addStatement("field.set(obj, value)")
                .build();
        
        MethodSpec invokeMethodMethod = MethodSpec.methodBuilder("invokeMethod")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addTypeVariable(TypeVariableName.get("T"))
                .returns(TypeVariableName.get("T"))
                .addException(ReflectiveOperationException.class)
                .addParameter(Object.class, "obj")
                .addParameter(String.class, "methodName")
                .addParameter(ArrayTypeName.of(Object.class), "args")
                .addStatement("$T[] parameterTypes = new $T[args.length]", Class.class, Class.class)
                .beginControlFlow("for (int i = 0; i < args.length; i++)")
                .addStatement("parameterTypes[i] = args[i] != null ? args[i].getClass() : Object.class")
                .endControlFlow()
                .addStatement("$T method = obj.getClass().getDeclaredMethod(methodName, parameterTypes)", Method.class)
                .addStatement("method.setAccessible(true)")
                .addStatement("return ($T) method.invoke(obj, args)", TypeVariableName.get("T"))
                .build();
        
        MethodSpec createInstanceMethod = MethodSpec.methodBuilder("createInstance")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addTypeVariable(TypeVariableName.get("T"))
                .returns(TypeVariableName.get("T"))
                .addException(ReflectiveOperationException.class)
                .addParameter(Class.class, "clazz")
                .addParameter(ArrayTypeName.of(Object.class), "args")
                .addStatement("$T[] parameterTypes = new $T[args.length]", Class.class, Class.class)
                .beginControlFlow("for (int i = 0; i < args.length; i++)")
                .addStatement("parameterTypes[i] = args[i] != null ? args[i].getClass() : Object.class")
                .endControlFlow()
                .addStatement("$T constructor = clazz.getDeclaredConstructor(parameterTypes)", Constructor.class)
                .addStatement("constructor.setAccessible(true)")
                .addStatement("return ($T) constructor.newInstance(args)", TypeVariableName.get("T"))
                .build();
        
        // 创建类
        TypeSpec reflectionUtilsClass = TypeSpec.classBuilder("ReflectionUtils")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(MethodSpec.constructorBuilder()
                        .addModifiers(Modifier.PRIVATE)
                        .addStatement("throw new $T($S)", UnsupportedOperationException.class, "Utility class")
                        .build())
                .addMethod(getFieldValueMethod)
                .addMethod(setFieldValueMethod)
                .addMethod(invokeMethodMethod)
                .addMethod(createInstanceMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.reflection", reflectionUtilsClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("生成ReflectionUtils类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成ReflectionUtils.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成动态代理类
     */
    public static void generateDynamicProxyClass() {
        // 创建接口
        TypeSpec serviceInterface = TypeSpec.interfaceBuilder("Service")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(MethodSpec.methodBuilder("execute")
                        .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                        .returns(String.class)
                        .addParameter(String.class, "input")
                        .build())
                .build();
        
        // 创建代理处理器类
        long[] args = new long[0];
        TypeSpec invocationHandlerClass = TypeSpec.classBuilder("LoggingInvocationHandler")
                .addModifiers(Modifier.PUBLIC)
                .addSuperinterface(InvocationHandler.class)
                .addField(FieldSpec.builder(Object.class, "target")
                        .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
                        .build())
                .addMethod(MethodSpec.constructorBuilder()
                        .addModifiers(Modifier.PUBLIC)
                        .addParameter(Object.class, "target")
                        .addStatement("this.target = target")
                        .build())
                .addMethod(MethodSpec.methodBuilder("invoke")
                        .addAnnotation(Override.class)
                        .addModifiers(Modifier.PUBLIC)
                        .returns(Object.class)
                        .addParameter(Object.class, "proxy")
                        .addParameter(Method.class, "method")
                        .addParameter(ArrayTypeName.of(Object.class), "args")
                        .addException(Throwable.class)
                        .addStatement("$T.out.println($S + method.getName() + $S)", 
                                System.class, "Before invoking method: ", " with args: " + Arrays.toString(args))
                        .addStatement("Object result = method.invoke(target, args)")
                        .addStatement("$T.out.println($S + method.getName() + $S + result)", 
                                System.class, "After invoking method: ", " returned: ")
                        .addStatement("return result")
                        .build())
                .build();
        
        // 创建代理工厂类
        TypeSpec proxyFactoryClass = TypeSpec.classBuilder("ProxyFactory")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(MethodSpec.methodBuilder("createProxy")
                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                        .addTypeVariable(TypeVariableName.get("T"))
                        .returns(TypeVariableName.get("T"))
                        .addParameter(TypeVariableName.get("T"), "target")
                        .addParameter(Class.class, "interfaceClass")
                        .addStatement("return ($T) $T.newProxyInstance(\n" +
                                "        target.getClass().getClassLoader(),\n" +
                                "        new $T[]{interfaceClass},\n" +
                                "        new LoggingInvocationHandler(target))",
                                TypeVariableName.get("T"), Proxy.class, Class.class)
                        .build())
                .build();
        
        // 创建示例类
        TypeSpec exampleClass = TypeSpec.classBuilder("ExampleService")
                .addModifiers(Modifier.PUBLIC)
                .addSuperinterface(ClassName.get("", "Service"))
                .addMethod(MethodSpec.methodBuilder("execute")
                        .addAnnotation(Override.class)
                        .addModifiers(Modifier.PUBLIC)
                        .returns(String.class)
                        .addParameter(String.class, "input")
                        .addStatement("return $S + input.toUpperCase()", "Processed: ")
                        .build())
                .build();
        
        // 创建测试方法
        MethodSpec testMethod = MethodSpec.methodBuilder("testProxy")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addStatement("$T realService = new ExampleService()", ClassName.get("", "Service"))
                .addStatement("$T proxy = ProxyFactory.createProxy(realService, Service.class)", 
                        ClassName.get("", "Service"))
                .addStatement("$T result = proxy.execute($S)", String.class, "test")
                .addStatement("$T.out.println(result)", System.class)
                .build();
        
        // 创建测试类
        TypeSpec testClass = TypeSpec.classBuilder("ProxyTest")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(testMethod)
                .addMethod(MethodSpec.methodBuilder("main")
                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                        .addParameter(ArrayTypeName.of(String.class), "args")
                        .returns(void.class)
                        .addStatement("testProxy()")
                        .build())
                .build();
        
        // 创建Java文件
        JavaFile serviceFile = JavaFile.builder("com.example.proxy", serviceInterface)
                .build();
        
        JavaFile handlerFile = JavaFile.builder("com.example.proxy", invocationHandlerClass)
                .build();
        
        JavaFile factoryFile = JavaFile.builder("com.example.proxy", proxyFactoryClass)
                .build();
        
        JavaFile exampleFile = JavaFile.builder("com.example.proxy", exampleClass)
                .build();
        
        JavaFile testFile = JavaFile.builder("com.example.proxy", testClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成Service接口:");
            serviceFile.writeTo(System.out);
            
            System.out.println("\n生成LoggingInvocationHandler类:");
            handlerFile.writeTo(System.out);
            
            System.out.println("\n生成ProxyFactory类:");
            factoryFile.writeTo(System.out);
            
            System.out.println("\n生成ExampleService类:");
            exampleFile.writeTo(System.out);
            
            System.out.println("\n生成ProxyTest类:");
            testFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            serviceFile.writeTo(new File(path.toUri()));
            handlerFile.writeTo(new File(path.toUri()));
            factoryFile.writeTo(new File(path.toUri()));
            exampleFile.writeTo(new File(path.toUri()));
            testFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成所有代理相关文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}